﻿#include "jscrollareauvlmengine.h"

#include <QTECore/qteglobal.h>
#include <QTEGui/juvlmengine.h>

#include <QAbstractScrollArea>
#include <QAbstractItemView>
#include <QScrollBar>

#include <QTimerEvent>
#include <QTimer>

namespace QTE
{

class Q_DECL_HIDDEN JScrollAreaUVLMEnginePrivate : public QObject
{
public:
	JScrollAreaUVLMEnginePrivate(QAbstractScrollArea *scrollArea, bool autoScrollBar);
	~JScrollAreaUVLMEnginePrivate() override;

public:
	void mousePressed(const QPoint &pos);
	void mouseRelease();
	bool mouseMove(const QPoint &pos);

public:
	void startInertia(const QPoint &speed); //开始惯性(定时器)
	void startScrollBarTimer();
	void killScrollBarTimer();
	void scrollBarInit();

public:
	void setAutoScrollBar(bool enable);
	void setEnabled(bool enable);

private:
	void timerEvent(QTimerEvent *event) override;
	bool eventFilter(QObject *object, QEvent *event) override;

public:
	QAbstractScrollArea *q_ptr;
	QWidget *m_viewport;
	QTimer *m_timer;

	QString m_SHBShowStyleSheet;
	QString m_SVBShowStyleSheet;
	const QString m_SHBHideStyleSheet;
	const QString m_SVBHideStyleSheet;

	bool m_SHBIsShow = true;
	bool m_SVBIsShow = true;
	bool m_autoHideBar = true;

	int m_hideTimerID = -1;
	bool m_first = true;
};

static QHash<const QAbstractScrollArea*, JScrollAreaUVLMEnginePrivate*> &gUVLMEngineHash()
{
	static QHash<const QAbstractScrollArea*, JScrollAreaUVLMEnginePrivate*> hash;
	return hash;
}

JScrollAreaUVLMEnginePrivate::
JScrollAreaUVLMEnginePrivate(QAbstractScrollArea *scrollArea, bool autoScrollBar) :
	QObject(scrollArea),
	q_ptr(scrollArea),
	m_timer(new QTimer(this)),
	m_SHBHideStyleSheet(
		"QScrollBar:horizontal {"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"	width: %1px;"
		"}"
		"QScrollBar::handle:horizontal {"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"	width: %1px;"
		"}"
		"QScrollBar::add-page:horizontal {"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"}"
		"QScrollBar::sub-page:horizontal {"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"}"
		"QScrollBar::sub-line:horizontal {"
		"	subcontrol-origin: margin;"
		"	border: none;"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"	height: 0px;"
		"}"
		"QScrollBar::up-arrow:horizontal {"
		"	subcontrol-origin: margin;"
		"	border: none;"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"	height: 0px;"
		"}"
		"QScrollBar::down-arrow:horizontal {"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"}"
		"QScrollBar::add-line:horizontal {"
		"	subcontrol-origin: margin;"
		"	border: none;"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"	height: 0px;"
		"}"),
	m_SVBHideStyleSheet(
		"QScrollBar:vertical {"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"	width: %1px;"
		"}"
		"QScrollBar::handle:vertical {"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"	width: %1px;"
		"}"
		"QScrollBar::add-page:vertical {"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"}"
		"QScrollBar::sub-page:vertical {"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"}"
		"QScrollBar::sub-line:vertical {"
		"	subcontrol-origin: margin;"
		"	border: none;"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"	height: 0px;"
		"}"
		"QScrollBar::up-arrow:vertical {"
		"	subcontrol-origin: margin;"
		"	border: none;"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"	height: 0px;"
		"}"
		"QScrollBar::down-arrow:vertical {"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"}"
		"QScrollBar::add-line:vertical {"
		"	subcontrol-origin: margin;"
		"	border: none;"
		"	color: rgba(255,255,255,0);"
		"	background-color: rgba(255,255,255,0);"
		"	height: 0px;"
		"}")
{
	m_viewport = q_ptr->viewport();
	q_ptr->installEventFilter(this);

	setAutoScrollBar(autoScrollBar);
	setEnabled(true);

	connect(m_timer, &QTimer::timeout, [this]()
	{
		delete m_timer;
		m_timer = nullptr;

		q_ptr->setSizeAdjustPolicy(QAbstractScrollArea::AdjustIgnored);
		auto view = J_OCT(QAbstractItemView*, q_ptr);

		if( view != nullptr )
		{
			view->setAutoScroll(false);
			view->setDragDropMode(QAbstractItemView::NoDragDrop);
			view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
			view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
		}
	});
	m_timer->start(1);

	gUVLMEngineHash().insert(scrollArea, this);
}

JScrollAreaUVLMEnginePrivate::~JScrollAreaUVLMEnginePrivate()
{
	gUVLMEngineHash().remove(q_ptr);
}

void JScrollAreaUVLMEnginePrivate::startInertia(const QPoint &speed)
{
	killScrollBarTimer();

	// x速度模
	int x_spd = speed.x();
	if( x_spd < 0 )
		x_spd = -x_spd;

	if( m_autoHideBar and x_spd > 12 and m_SHBIsShow == false )
	{
		m_SHBIsShow = true;
		q_ptr->horizontalScrollBar()->setStyleSheet(m_SHBShowStyleSheet);
	}

	// y速度模
	int y_spd = speed.y();
	if( y_spd < 0 )
		y_spd = -y_spd;

	if( m_autoHideBar and y_spd > 12 and m_SVBIsShow == false )
	{
		m_SVBIsShow = true;
		q_ptr->verticalScrollBar()->setStyleSheet(m_SVBShowStyleSheet);
	}
}

void JScrollAreaUVLMEnginePrivate::startScrollBarTimer()
{
	if( m_autoHideBar and m_hideTimerID == -1 and (m_SHBIsShow or m_SVBIsShow) )
		m_hideTimerID = startTimer(1500);
}

void JScrollAreaUVLMEnginePrivate::killScrollBarTimer()
{
	if( m_hideTimerID != -1 )
	{
		killTimer(m_hideTimerID);
		m_hideTimerID = -1;

		if( m_SVBIsShow )
		{
			m_SVBShowStyleSheet = q_ptr->verticalScrollBar()->styleSheet();
			q_ptr->verticalScrollBar()->setStyleSheet(m_SVBHideStyleSheet.arg(q_ptr->verticalScrollBar()->width()));
			m_SVBIsShow = false;
		}

		if( m_SHBIsShow )
		{
			m_SHBShowStyleSheet = q_ptr->horizontalScrollBar()->styleSheet();
			q_ptr->horizontalScrollBar()->setStyleSheet(m_SHBHideStyleSheet.arg(q_ptr->horizontalScrollBar()->height()));
			m_SHBIsShow = false;
		}
	}
}

void JScrollAreaUVLMEnginePrivate::scrollBarInit()
{
	if( q_ptr->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOn )
		q_ptr->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
	if( q_ptr->horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOn )
		q_ptr->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

	m_SVBShowStyleSheet = q_ptr->verticalScrollBar()->styleSheet();
	m_SHBShowStyleSheet = q_ptr->horizontalScrollBar()->styleSheet();

	q_ptr->verticalScrollBar()->setStyleSheet(m_SVBHideStyleSheet.arg(q_ptr->verticalScrollBar()->width()));
	q_ptr->horizontalScrollBar()->setStyleSheet(m_SHBHideStyleSheet.arg(q_ptr->horizontalScrollBar()->height()));

	m_SHBIsShow = m_SVBIsShow = false;
}

void JScrollAreaUVLMEnginePrivate::setAutoScrollBar(bool enable)
{
	if( m_autoHideBar == enable )
		return ;
	m_autoHideBar = enable;

	if( enable )
	{
		m_SHBIsShow = m_SVBIsShow = false;

		m_SVBShowStyleSheet = q_ptr->verticalScrollBar()->styleSheet();
		m_SHBShowStyleSheet = q_ptr->horizontalScrollBar()->styleSheet();

		q_ptr->verticalScrollBar()->setStyleSheet(m_SVBHideStyleSheet.arg(q_ptr->verticalScrollBar()->width()));
		q_ptr->horizontalScrollBar()->setStyleSheet(m_SHBHideStyleSheet.arg(q_ptr->horizontalScrollBar()->height()));

		q_ptr->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
		q_ptr->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
	}

	else
	{
		killScrollBarTimer();

		q_ptr->verticalScrollBar()->setStyleSheet(m_SVBShowStyleSheet);
		q_ptr->horizontalScrollBar()->setStyleSheet(m_SHBShowStyleSheet);

		m_SHBIsShow = m_SVBIsShow = true;
	}

}

void JScrollAreaUVLMEnginePrivate::setEnabled(bool enable)
{
	if( enable )
	{
		JUVLMEngine::Bind bind;
		bind.widget = q_ptr->viewport();

		bind.actionTriggered = [this](const QPoint &delta){
			mousePressed(delta);
		};

		bind.running = [this](const QPoint &delta){
			return mouseMove(delta);
		};

		bind.inertanceStarted = [this](const QPoint &speed){
			startInertia(speed);
		};

		bind.stop = [this](){
			mouseRelease();
		};

		JUVLMEngine::globalInstance().bind(bind);
	}

	else if( m_viewport )
		JUVLMEngine::globalInstance().unbound(m_viewport);
}

void JScrollAreaUVLMEnginePrivate::mousePressed(const QPoint&)
{

}

void JScrollAreaUVLMEnginePrivate::mouseRelease()
{
	if( m_autoHideBar and m_hideTimerID == -1 and (m_SHBIsShow or m_SVBIsShow) )
		m_hideTimerID = startTimer(1500);
}

bool JScrollAreaUVLMEnginePrivate::mouseMove(const QPoint &speed)
{
	int res = 0;

	int next_vsy = q_ptr->verticalScrollBar()->value() + speed.y();
	int max_vsy = q_ptr->verticalScrollBar()->maximum();

	if( next_vsy < 0 )
	{
		++res;
		q_ptr->verticalScrollBar()->setSliderPosition(0);
	}
	else if( next_vsy > max_vsy )
	{
		++res;
		q_ptr->verticalScrollBar()->setSliderPosition(max_vsy);
	}
	else
		q_ptr->verticalScrollBar()->setSliderPosition(next_vsy);

	int next_vsx = q_ptr->horizontalScrollBar()->value() + speed.x();
	int max_vsx = q_ptr->horizontalScrollBar()->maximum();

	if( next_vsx < 0 )
	{
		++res;
		q_ptr->horizontalScrollBar()->setSliderPosition(0);
	}
	else if( next_vsx > max_vsx )
	{
		++res;
		q_ptr->horizontalScrollBar()->setSliderPosition(max_vsx);
	}
	else
		q_ptr->horizontalScrollBar()->setSliderPosition(next_vsx);

	return res < 2;
}

void JScrollAreaUVLMEnginePrivate::timerEvent(QTimerEvent *event)
{
	if( event->timerId() == m_hideTimerID )
		killScrollBarTimer();
}

bool JScrollAreaUVLMEnginePrivate::eventFilter(QObject *object, QEvent *event)
{
	auto qasa = J_OCT(QAbstractScrollArea*, object);
	if( qasa != q_ptr )
		return false;

	if( event->type() == QEvent::Show or event->type() == QEvent::Hide )
	{
		if( m_autoHideBar )
		{
			killScrollBarTimer();
			scrollBarInit();
		}
	}

	else if( event->type() == QEvent::ChildAdded or
			 event->type() == QEvent::ChildRemoved or
			 event->type() == QEvent::ChildPolished )
	{
		if( q_ptr->viewport() != m_viewport )
		{
			setEnabled(false);
			setEnabled(true);
			m_viewport = q_ptr->viewport();
		}
	}

	return false;
}

/*----------------------------------------------------------------------------*/

JScrollAreaUVLMEngine::JScrollAreaUVLMEngine(QAbstractScrollArea *scrollArea, bool autoScrollBar) :
	d_ptr(new JScrollAreaUVLMEnginePrivate(scrollArea, autoScrollBar))
{

}

JScrollAreaUVLMEngine::~JScrollAreaUVLMEngine()
{
	delete d_ptr;
}

void JScrollAreaUVLMEngine::setAutoScrollBar(bool enable)
{
	d_ptr->setAutoScrollBar(enable);
}

void JScrollAreaUVLMEngine::setUVLMEEnabled(bool enable)
{
	d_ptr->setEnabled(enable);
}

bool JScrollAreaUVLMEngine::autoScrollBar()
{
	return d_ptr->m_autoHideBar;
}

bool JScrollAreaUVLMEngine::isUVLMEEnabled()
{
	return JUVLMEngine::globalInstance().isBind(d_ptr->q_ptr);
}

void JScrollAreaUVLMEngine::bind(QAbstractScrollArea *scrollArea, bool autoScrollBar)
{
	if( gUVLMEngineHash().find(scrollArea) == gUVLMEngineHash().end() )
		new JScrollAreaUVLMEnginePrivate(scrollArea, autoScrollBar);
}

void JScrollAreaUVLMEngine::unbound(const QAbstractScrollArea *scrollArea)
{
	auto it = gUVLMEngineHash().find(scrollArea);

	if( it != gUVLMEngineHash().end() )
		delete *it;
}

void JScrollAreaUVLMEngine::setAutoScrollBar(const QAbstractScrollArea *scrollArea, bool enable)
{
	auto it = gUVLMEngineHash().find(scrollArea);

	if( it != gUVLMEngineHash().end() )
		(*it)->setAutoScrollBar(enable);
}

void JScrollAreaUVLMEngine::setUVLMEEnabled(const QAbstractScrollArea *scrollArea, bool enable)
{
	auto it = gUVLMEngineHash().find(scrollArea);

	if( it != gUVLMEngineHash().end() )
		(*it)->setEnabled(enable);
}

void JScrollAreaUVLMEngine::setRefreshRate(int rate)
{
	JUVLMEngine::globalInstance().setRefreshRate(rate);
}

bool JScrollAreaUVLMEngine::isBind(const QAbstractScrollArea *scrollArea)
{
	return gUVLMEngineHash().find(scrollArea) != gUVLMEngineHash().end();
}

bool JScrollAreaUVLMEngine::autoScrollBar(const QAbstractScrollArea *scrollArea)
{
	auto it = gUVLMEngineHash().find(scrollArea);

	if( it == gUVLMEngineHash().end() )
		return false;

	return (*it)->m_autoHideBar;
}

bool JScrollAreaUVLMEngine::isUVLMEEnabled(const QAbstractScrollArea *scrollArea)
{
	auto it = gUVLMEngineHash().find(scrollArea);

	if( it == gUVLMEngineHash().end() )
		return false;

	return JUVLMEngine::globalInstance().isBind((*it)->q_ptr);
}

int JScrollAreaUVLMEngine::refreshRate()
{
	return JUVLMEngine::globalInstance().refreshRate();
}

} //namespace QTE
